<template>
	<view class="TuningGuqin">
		<view class="topContent">
			<image class="topbg" src="@/static/image/topbg.png" mode=""></image>
			<view class="TuningGuqinBox">
				<view class="titleBox">
					古琴调音器
				</view>

				<view class="DashboardContent">
					<image class="DashboardImg" src="/static/image/Dashboard.png" mode=""></image>
					<view class="keyName">
						{{keyName}}
					</view>
					<view class="pointer-wrapper">
						<image class="PointerImg" :style="{ transform: 'rotate(' + needleAngle + 'deg)' }"
							src="/static/image/Pointer.png" mode=""></image>
					</view>
				</view>
			</view>

		</view>
		<view class="TuningDesc">
			<view class="stringBox">
				<view class="stringItem" v-for="(item, index) in 7" :key="index">
					<image class="stringimg" :class="{ stringimged: stringCurrindex == index }"
						src="@/static/image/string.png" mode="">
					</image>
				</view>
			</view>

			<view class="desc">
				正在调{{numberString}}弦，音名：{{keyName}}，钢琴键位：{{keyName}}，频率：{{targetFreq}}Hz
			</view>

			<view class="setBox">
				<view class="setItem" v-for="(item, index) in 7" :key="index">
					<image v-if="stringCurrindex == index" class="setimg" src="@/static/image/setIcon.gif" mode="">
					</image>
				</view>
			</view>
		</view>

		<view class="jeanContent">
			<view class="TuningBox">
				<image src="@/static/image/Tuningbg.png" mode=""></image>
			</view>

			<view class="jeanContent_inner">
				<view class="stringItem" v-for="(item, index) in 7" :key="index" @click="handleChangeString(index)">
				</view>
			</view>

			<view class="stringList">
				<view class="stringItem" :class="{ stringItems: stringCurrindex == index }"
					v-for="(item, index) in stringList" :key="index" @click="handleChangeString(index)">
					{{ item.title }}
				</view>
			</view>

			<view v-if="!isListening" class="statrBtn" @click="startListening">
				开始
			</view>
			<view v-else class="statrBtn endbtn" @click="stopListening">
				停止
			</view>
		</view>

		<view class="freq-setting">
			<text class="lable">频率设置(Hz):</text>
			<view class="slider-wrap">
				<slider min="0" max="450" step="1" v-model="freq" @changing="onFreqChanging" @change="onFreqChange"
					show-value :value="freq" block-size="15" blockColor="#00DDA5" activeColor="#00DDA5"
					backgroundColor="#ffffff" style="width: 424rpx; height: 36rpx; border-radius: 8rpx;" />
			</view>
		</view>

		<view class="typeContent">
			<view class="lable">
				切换调试：
			</view>
			<view class="typeBox" @click="showTypeSelect">
				{{ typeName }}
			</view>
		</view>



		<view class="bannerContent">
			banner
		</view>

		<view class="Instructions">
			<u-parse :content="content"></u-parse>
		</view>

		<u-picker :show="showTypePicker" :columns="typeList" confirmColor="#07C160" @cancel="showTypePicker = false"
			@confirm="confirmType"></u-picker>

	</view>
</template>

<script>
	import {
		analyzeFrequency,
	} from '@/utils/audioAnalyzr.js'
	import {
		typeLists,
		Contenttext
	} from "../../api/api.js"
	export default {
		data() {
			return {
				stringCurrindex: 0,
				stringList: [],
				startFlage: false,
				freq: 200,
				typeName: "",
				typeData: [],
				typeList: [
					[]
				],
				showTypePicker: false,
				lastFreq: 0,
				freqStableCount: 0,
				isStopping: false,
				currentFreq: 0,
				targetFreq: 0, //Hz
				needleAngle: 0,
				targetNeedleAngle: 0,
				needleAnimFrame: null,
				statusText: '等待检测...',
				isListening: false,
				recorderManager: null,
				onFrameRecordedRegistered: false,
				keyName: "",
				keyHz: "",
				numberString: "一",
				content: "",
			}
		},
		onReady() {},
		onLoad() {
			this.handleTypeLists()
			this.handleGetContenttext()
		},
		methods: {
			async handleTypeLists() {
				let res = await typeLists()
				this.typeData = res.data
				res.data.forEach((item, index) => {
					this.typeList[0].push(item.name)
				})
				this.stringList = this.typeData[0].title
				this.typeName = this.typeData[0].name
				this.keyName = this.stringList[0].title
				this.targetFreq = this.stringList[0].intro
			},
			async handleGetContenttext() {
				let res = await Contenttext()
				this.content = res.data
			},
			handleChangeString(index) {
				this.stringCurrindex = index
				this.keyName = this.stringList[index].title
				this.targetFreq = this.stringList[index].intro
				if (index == 0) {
					this.numberString = '一'
				}
				if (index == 1) {
					this.numberString = '二'
				}
				if (index == 2) {
					this.numberString = '三'
				}
				if (index == 3) {
					this.numberString = '四'
				}
				if (index == 4) {
					this.numberString = '五'
				}
				if (index == 5) {
					this.numberString = '六'
				}
				if (index == 6) {
					this.numberString = '七'
				}
			},
			handlChangeStart() {
				this.startFlage = !this.startFlage
			},
			onFreqChanging(e) {
				this.freq = e.detail.value
			},
			onFreqChange(e) {
				this.freq = e.detail.value
				// 这里可以做频率变更后的处理
			},
			showTypeSelect() {
				this.showTypePicker = true;
			},
			confirmType(e) {
				this.typeName = e.value[0]
				this.showTypePicker = false
				this.stringList = this.typeData[e.indexs[0]].title
				this.stringCurrindex = 0
				this.keyName = this.stringList[0].title
				this.targetFreq = this.stringList[0].intro
			},

			async startListening() {
				if (this.isListening) return;
				this.isListening = true;
				this.recorderManager = uni.getRecorderManager();
				this.statusText = '检测中...';
				if (!this.onFrameRecordedRegistered) {
					this.recorderManager.onFrameRecorded((res) => {
						if (!this.isListening) return;
						const arrayBuffer = res.frameBuffer;
						const int16Data = new Int16Array(arrayBuffer);
						let floatBuffer = new Float32Array(int16Data.length);
						for (let i = 0; i < int16Data.length; i++) {
							floatBuffer[i] = int16Data[i] / 32768;
						}
						let shortBuffer = floatBuffer.length > 4096 ? floatBuffer.slice(0, 4096) : floatBuffer;
						const frequency = analyzeFrequency(shortBuffer, 44100);
						if (isNaN(frequency) || frequency <= 0 || frequency > 2000) {
							this.currentFreq = '-';
							this.statusText = '无信号';
							this.needleAngle = -70;
						} else {
							this.updateUI(frequency);
						}
					});
					this.onFrameRecordedRegistered = true;
				}
				this.recorderManager.start({
					format: 'PCM',
					sampleRate: 44100,
					numberOfChannels: 1,
					encodeBitRate: 96000,
					frameSize: 1
				});
			},

			stopListening() {
				if (!this.isListening) return;
				this.isListening = false;
				this.statusText = '已停止';
				if (this.recorderManager) {
					this.recorderManager.stop();
				}
			},

			updateUI(detectedFreq) {
				const diff = detectedFreq - this.targetFreq
				let angle = 0
				let status = ''
				const minAngle = -70
				const centerAngle = 0
				const maxAngle = 70
				const maxDiff = this.targetFreq * 0.1 // ±10%的范围

				if (diff <= -maxDiff) {
					angle = minAngle
				} else if (diff >= maxDiff) {
					angle = maxAngle
				} else {
					angle = centerAngle + (diff / maxDiff) * (maxAngle - centerAngle)
				}

				if (Math.abs(diff) < this.targetFreq * 0.01) {
					status = '音高准确'
				} else if (diff > 0) {
					status = '偏高'
				} else {
					status = '偏低'
				}
				this.currentFreq = detectedFreq.toFixed(2)
				this.targetNeedleAngle = angle;
				this.statusText = status;
				this.animateNeedle();
			},

			animateNeedle() {
				if (this.needleAnimFrame) this.cancelAnimationFrame(this.needleAnimFrame);
				const animate = () => {
					const SMOOTH = 0.25; // 越大越快，0.2~0.4都可以试
					const diff = this.targetNeedleAngle - this.needleAngle;
					if (Math.abs(diff) < 0.1) {
						this.needleAngle = this.targetNeedleAngle;
						this.needleAnimFrame = null;
						return;
					}
					this.needleAngle += diff * SMOOTH;
					this.needleAnimFrame = this.requestAnimationFrame(animate);
				};
				animate();
			},

			requestAnimationFrame(cb) {
				return setTimeout(cb, 16);
			},

			cancelAnimationFrame(id) {
				clearTimeout(id);
			},

		},
		beforeDestroy() {
			this.stopListening()
		}
	}
</script>

<style scoped lang="scss">
	.TuningGuqin {
		width: 100%;
		min-height: 100vh;
		background: #F5F6F8;

		.topContent {
			width: 100%;
			position: relative;

			.topbg {
				width: 100%;
				height: 480rpx;
			}

			.TuningGuqinBox {
				width: 100%;
				height: 480rpx;
				position: absolute;
				top: 0;
				padding-top: 108rpx;
				box-sizing: border-box;

				.titleBox {
					width: 100%;
					font-size: 34rpx;
					color: #000000;
					line-height: 40rpx;
					text-align: center;
				}

				.DashboardContent {
					width: 690rpx;
					height: 292rpx;
					// background-color: pink;
					margin: 45rpx auto 0;
					position: relative;

					.DashboardImg {
						width: 100%;
						height: 280rpx;
					}

					.keyName {
						width: 100%;
						font-family: PingFangSC, PingFang SC;
						font-weight: 600;
						font-size: 96rpx;
						color: rgba(51, 51, 51, 0.9);
						line-height: 134rpx;
						text-align: center;
						position: absolute;
						top: 96rpx;
					}

					.pointer-wrapper {
						width: 8rpx;
						height: 296rpx;
						position: absolute;
						left: 49.5%;
						bottom: 0; // 让容器底部对齐
						overflow: visible;
						// 这里不需要 transform-origin，图片本身设置即可
					}

					.PointerImg {
						width: 8rpx;
						height: 296rpx;
						transform-origin: 50% 100%;
					}

				}
			}

		}

		.TuningDesc {
			width: 100%;
			height: 218rpx;
			position: relative;

			.stringBox {
				width: 100%;
				padding: 0 100rpx;
				box-sizing: border-box;
				margin: auto;
				display: flex;
				align-items: center;
				justify-content: space-between;

				.stringItem {
					width: 8rpx;

					.stringimg {
						width: 8rpx;
						height: 218rpx;
					}

					.stringimged {
						box-shadow: 0 4px 12px rgba(0, 0, 0, 0.48);
						border-radius: 8px;
					}
				}
			}

			.desc {
				width: 606rpx;
				font-weight: 550;
				font-size: 32rpx;
				color: #333333;
				line-height: 44rpx;
				text-align: center;
				position: absolute;
				top: 32rpx;
				left: 72rpx;
			}

			.setBox {
				width: 100%;
				display: flex;
				align-items: center;
				padding: 0 58rpx;
				box-sizing: border-box;
				position: absolute;
				bottom: 0;

				.setItem {
					width: 92rpx;

					.setimg {
						width: 92rpx;
						height: 90rpx;
					}
				}
			}

		}

		.jeanContent {
			width: 100%;
			padding: 0 30rpx;
			box-sizing: border-box;
			position: relative;

			.TuningBox {
				margin: auto;

				image {
					width: 690rpx;
					height: 672rpx;
				}
			}

			.jeanContent_inner {
				width: 690rpx;
				height: 592rpx;
				// background-color: pink ;
				position: absolute;
				bottom: 0;
				padding: 0 46rpx;
				box-sizing: border-box;
				display: flex;
				align-items: center;
				justify-content: space-between;

				.stringItem {
					width: 56rpx;
					height: 572rpx;
					// background-color: green;
				}
			}

			.stringList {
				width: 690rpx;
				display: flex;
				align-items: center;
				justify-content: space-between;
				padding: 0 46rpx;
				box-sizing: border-box;
				position: absolute;
				top: 214rpx;

				.stringItem {
					width: 56rpx;
					height: 68rpx;
					background: #FFFFFF;
					border-radius: 10rpx;
					opacity: 0.5;
					font-weight: 400;
					font-size: 36rpx;
					color: #000000;
					line-height: 68rpx;
					text-align: center;
				}

				.stringItems {
					background: #FFFFFF;
					opacity: 1;
				}
			}

			.statrBtn {
				width: 296rpx;
				height: 88rpx;
				background: #00BADD;
				border-radius: 47rpx;
				font-weight: 600;
				font-size: 36rpx;
				color: #FFFFFF;
				line-height: 88rpx;
				text-align: center;
				position: absolute;
				left: 50%;
				margin-left: -148rpx;
				bottom: 120rpx;
			}

			.endbtn {
				background: #C00303;
			}
		}

		.freq-setting {
			width: 100%;
			padding: 0 30rpx;
			box-sizing: border-box;
			display: flex;
			align-items: center;
			margin: 40rpx 0;

			.lable {
				font-weight: 550;
				font-size: 28rpx;
				color: #333333;
				line-height: 40rpx;
			}
		}

		.slider-wrap {
			width: 300rpx;
			/* 固定长度 */
			margin: 0 20rpx;
		}
	}


	.typeContent {
		width: 100%;
		padding: 0 30rpx;
		box-sizing: border-box;
		display: flex;
		align-items: center;

		.lable {
			font-weight: 550;
			font-size: 28rpx;
			color: #333333;
			line-height: 40rpx;
		}

		.typeBox {
			width: 224rpx;
			height: 72rpx;
			background: #00DDA5;
			border-radius: 8rpx;
			font-weight: 600;
			font-size: 36rpx;
			color: #FFFFFF;
			line-height: 72rpx;
			text-align: center;
		}

	}

	.bannerContent {
		width: 690rpx;
		height: 200rpx;
		background: #6F0404;
		border-radius: 20rpx;
		margin: 32rpx auto;
		font-weight: 600;
		font-size: 36rpx;
		color: #FFFFFF;
		line-height: 200rpx;
		text-align: center;
	}

	.Instructions {
		width: 100%;
		padding: 30rpx;
		padding-bottom: 50rpx;
		box-sizing: border-box;
		background-color: #ffffff;

		.title {
			font-weight: 550;
			font-size: 32rpx;
			color: #000000;
			line-height: 44rpx;
		}

		.content {
			font-weight: 400;
			font-size: 32rpx;
			color: #000000;
			line-height: 50rpx;
			margin-top: 16rpx;
		}
	}

	.tuner-meter-canvas {
		width: 600rpx;
		height: 300rpx;
		display: block;
		margin: 0 auto;
		// position: absolute;
		// left: 50%;
		// top: 40rpx;
		// transform: translateX(-50%);
		z-index: 2;
	}
</style>